--[[
@module mq2
@summary MQ-2烟雾传感器驱动
@version 1.0
@date    2024.01.01
@description 支持MQ-2烟雾传感器的读取和浓度检测

@usage
-- 注意:因使用了sys.wait()所有api需要在协程中使用
local mq2 = require "mq2"
local adc = require "adc"

sys.taskInit(function()
    -- 初始化ADC
    adc.open(adc.ADC0)
    
    -- 初始化MQ-2传感器(需要预热)
    if mq2.init() then
        log.info("mq2", "传感器初始化完成，开始预热5分钟...")
        
        -- 预热阶段
        mq2.preheat(300000) -- 5分钟预热
        
        -- 校准传感器在清洁空气中的基准值
        mq2.calibrate()
        
        log.info("mq2", "预热和校准完成，可以开始测量")
        
        -- 主循环
        while true do
            -- 读取烟雾浓度
            local value = mq2.get_value()
            local ppm = mq2.get_ppm()
            local status = mq2.get_status()
            
            log.info("mq2", "传感器原始值: " .. tostring(value))
            log.info("mq2", "估算浓度: " .. tostring(ppm) .. " ppm")
            log.info("mq2", "状态: " .. status)
            
            sys.wait(2000)
        end
    end
end)
]]

local mq2 = {}
local sys = require "sys"

-- 默认参数设置
local DEFAULT_ADC_CH = adc.ADC0  -- 默认ADC通道
local DEFAULT_LOAD_RESISTANCE = 5000000  -- 5MΩ 默认负载电阻
local DEFAULT_R0_ADJUST = 1.0     -- R0调整系数
local DEFAULT_THRESHOLD_LOW = 5000  -- 低阈值
local DEFAULT_THRESHOLD_HIGH = 15000 -- 高阈值

-- 传感器状态变量
local initialized = false
local r0 = 1000000 -- 清洁空气中的传感器电阻(初始默认值)
local adc_channel = DEFAULT_ADC_CH
local load_resistance = DEFAULT_LOAD_RESISTANCE
local threshold_low = DEFAULT_THRESHOLD_LOW
local threshold_high = DEFAULT_THRESHOLD_HIGH
local preheat_done = false

-- 内部函数：从ADC读取原始值
local function read_adc()
    if not adc.isOpen() then
        log.error("mq2", "ADC未初始化")
        return nil
    end
    
    local value = adc.read(adc_channel)
    if value then
        return value
    else
        log.error("mq2", "ADC读取失败")
        return nil
    end
end

-- 内部函数：计算传感器电阻
local function calculate_rs(adc_value)
    if not adc_value then return nil end
    
    -- 假设ADC参考电压为3.3V
    local voltage = (adc_value / 4095) * 3.3  -- 假设12位ADC
    
    -- 使用电压分压公式计算传感器电阻
    -- Rs = RL * (Vc - Vout) / Vout
    local rs = load_resistance * (3.3 - voltage) / voltage
    
    return rs
end

-- 内部函数：转换为PPM估算值
local function calculate_ppm(rs)
    if not rs or rs <= 0 or r0 <= 0 then return 0 end
    
    -- Rs/R0比值
    local ratio = rs / r0
    
    -- 使用经验公式估算PPM (这是一个简化的模型)
    -- 注意：实际应用中可能需要更精确的校准曲线
    if ratio <= 0 then return 0 end
    
    -- 这是一个近似公式，实际应用中应根据校准数据调整
    local ppm = math.exp(-math.log(ratio) / 0.6) * 1000
    
    -- 限制在传感器的有效范围内
    if ppm < 300 then ppm = 300 end
    if ppm > 10000 then ppm = 10000 end
    
    return ppm
end

--[[
mq2初始化
@api mq2.init(adc_ch, rl)
@number adc_ch ADC通道，默认使用adc.ADC0
@number rl 负载电阻值(欧姆)，默认5MΩ
@return bool 成功返回true
@usage
local ret = mq2.init(adc.ADC1, 4700000)
if ret then
    log.info("mq2", "初始化成功")
end
]]
function mq2.init(adc_ch, rl)
    adc_channel = adc_ch or DEFAULT_ADC_CH
    load_resistance = rl or DEFAULT_LOAD_RESISTANCE
    
    log.info("mq2", "初始化MQ-2传感器，ADC通道: " .. tostring(adc_channel) .. ", 负载电阻: " .. tostring(load_resistance) .. "Ω")
    
    initialized = true
    preheat_done = false
    return true
end

--[[
设置烟雾浓度阈值
@api mq2.set_threshold(low, high)
@number low 低阈值
@number high 高阈值
@usage
mq2.set_threshold(3000, 10000)
]]
function mq2.set_threshold(low, high)
    threshold_low = low or DEFAULT_THRESHOLD_LOW
    threshold_high = high or DEFAULT_THRESHOLD_HIGH
    log.info("mq2", "阈值设置: 低=" .. tostring(threshold_low) .. ", 高=" .. tostring(threshold_high))
end

--[[
预热传感器
@api mq2.preheat(duration_ms)
@number duration_ms 预热时间(毫秒)，建议不少于300000毫秒(5分钟)
@usage
mq2.preheat(300000) -- 预热5分钟
]]
function mq2.preheat(duration_ms)
    if not initialized then
        log.error("mq2", "传感器未初始化")
        return false
    end
    
    local duration = duration_ms or 300000 -- 默认5分钟
    log.info("mq2", "开始预热，持续时间: " .. tostring(duration) .. "毫秒")
    
    -- 预热过程中可以定期读取传感器以监测稳定性
    local steps = 10
    local step_time = duration / steps
    
    for i = 1, steps do
        log.info("mq2", "预热进度: " .. tostring(math.floor((i-1)/steps*100)) .. "%")
        sys.wait(step_time)
    end
    
    log.info("mq2", "预热完成")
    preheat_done = true
    return true
end

--[[
校准传感器在清洁空气中的基准值
@api mq2.calibrate(sample_count)
@number sample_count 采样次数，默认5次
@return number 校准后的R0值
@usage
local r0_value = mq2.calibrate()
]]
function mq2.calibrate(sample_count)
    if not initialized then
        log.error("mq2", "传感器未初始化")
        return nil
    end
    
    if not preheat_done then
        log.warn("mq2", "传感器未完成预热，可能影响校准精度")
    end
    
    local count = sample_count or 5
    local total_rs = 0
    
    log.info("mq2", "开始在清洁空气中校准，采样次数: " .. tostring(count))
    
    for i = 1, count do
        local adc_value = read_adc()
        if adc_value then
            local rs = calculate_rs(adc_value)
            if rs then
                total_rs = total_rs + rs
                log.info("mq2", "校准采样 " .. tostring(i) .. "/" .. tostring(count) .. ", Rs = " .. tostring(rs) .. "Ω")
            end
        end
        sys.wait(1000) -- 每次采样间隔1秒
    end
    
    -- 计算平均Rs值
    local avg_rs = total_rs / count
    
    -- 在清洁空气中，Rs/R0 ≈ 1，所以R0 ≈ Rs
    -- 使用调整系数进行微调
    r0 = avg_rs * DEFAULT_R0_ADJUST
    
    log.info("mq2", "校准完成，R0 = " .. tostring(r0) .. "Ω")
    return r0
end

--[[
读取传感器原始ADC值
@api mq2.get_value()
@return number ADC原始值
@usage
local value = mq2.get_value()
]]
function mq2.get_value()
    if not initialized then
        log.error("mq2", "传感器未初始化")
        return nil
    end
    
    local value = read_adc()
    return value
end

--[[
获取烟雾浓度估算值(PPM)
@api mq2.get_ppm()
@return number 估算的烟雾浓度(PPM)
@usage
local ppm = mq2.get_ppm()
]]
function mq2.get_ppm()
    if not initialized then
        log.error("mq2", "传感器未初始化")
        return nil
    end
    
    local adc_value = read_adc()
    if not adc_value then return nil end
    
    local rs = calculate_rs(adc_value)
    local ppm = calculate_ppm(rs)
    
    return ppm
end

--[[
获取传感器电阻值
@api mq2.get_rs()
@return number 传感器电阻值(欧姆)
@usage
local rs = mq2.get_rs()
]]
function mq2.get_rs()
    if not initialized then
        log.error("mq2", "传感器未初始化")
        return nil
    end
    
    local adc_value = read_adc()
    if not adc_value then return nil end
    
    local rs = calculate_rs(adc_value)
    return rs
end

--[[
获取传感器状态
@api mq2.get_status()
@return string 状态描述("正常", "低浓度", "高浓度")
@usage
local status = mq2.get_status()
]]
function mq2.get_status()
    local ppm = mq2.get_ppm()
    if not ppm then return "未知" end
    
    if ppm < threshold_low then
        return "正常"
    elseif ppm < threshold_high then
        return "低浓度"
    else
        return "高浓度"
    end
end

return mq2